module sw.commands;

import sw.parser, sw.global.defaultcommands, sw.unsupportedcommands, sw.siteconfig;
version(Wiki)import sw.wiki.wikicommands;
version(Upload)import sw.upload.uploadcommands;
version(BugTracking)import sw.bugtracking.bugtrackingcommands;

interface Commands{
  const static string prefix;
  //第二引数コマンドだとコマンドの中にコマンドが含まれる状態なので他のデザパタでないと無理
  //深い方から展開してくのがよさげ
  string value(SiteConfig* cfg,string name,string[] args,Cmd* cmd,uint hierarchy,string path);/*args.lengthで決める*/
}

class CommandsList{
  static Commands[5] cache;
  ///モードのリストを返す
  static Commands getCommandsList(string prefix){
    switch(prefix){
      version(Upload){
        case UploadCommands.prefix:
          if(cache[0] is null)cache[0]=new UploadCommands;
          return cache[0];
      }
      version(BugTracking){
        case BugTrackingCommands.prefix:
          if(cache[1] is null)cache[1]=new BugTrackingCommands;
          return cache[1];
      }
      version(Wiki){
        case WikiCommands.prefix:
          if(cache[2] is null)cache[2]=new WikiCommands;
          return cache[2];
      }
      case DefaultCommands.prefix:
          if(cache[3] is null)cache[3]=new DefaultCommands;
          return cache[3];
      default:
    }
    if(cache[4] is null)cache[4]=new UnsupportedCommands;
    return cache[4];
  }
}

import std.uri, nazo.string, std.regexp, std.zlib, std.contracts;

///引数変換用
///Bugs: パーサ部分でHTMLエスケープがされているのでわざわざここで使う必要は無い。
static struct ArgConv{
  static string unescape(string source){
    char[] re;
    re.length=source.length;
    uint n;
    uint ist;
    for(uint i;i<source.length;i++){
      char s=source[i];
      if(s=='&'){
        if(source[i..i+4]=="&lt;"){
          if(source[ist..i].length!=0)re[ist-n..i-n]=source[ist..i];
          re[i-n]='<';
          re.length=re.length-3;
          n+=3;
          i+=3;
          ist=i+1;
        }else if(source[i..i+4]=="&gt;"){
          if(source[ist..i].length!=0)re[ist-n..i-n]=source[ist..i];
          re[i-n]='>';
          re.length=re.length-3;
          n+=3;
          i+=3;
          ist=i+1;
        }else if(source[i..i+5]=="&amp;"){
          if(source[ist..i].length!=0)re[ist-n..i-n]=source[ist..i];
          re[i-n]='&';
          re.length=re.length-4;
          n+=4;
          i+=4;
          ist=i+1;
        }else if(source[i..i+6]=="&quot;"){
          if(source[ist..i].length!=0)re[ist-n..i-n]=source[ist..i];
          re[i-n]='"';
          re.length=re.length-5;
          n+=5;
          i+=5;
          ist=i+1;
        }else if(source[i..i+6]=="&apos;"){
          if(source[ist..i].length!=0)re[ist-n..i-n]=source[ist..i];
          re[i-n]='\'';
          re.length=re.length-5;
          n+=5;
          i+=5;
          ist=i+1;
        }
      }
      if(source[ist..$].length!=0)re[ist-n..$]=source[ist..$];
    }
    return assumeUnique(re);
  }
  ///属性用のエスケープ
  static string attrEscape(string org){return org;}
  ///ターゲット用のエスケープ
  static string hrefTargetEscape(string org){
    org=strip(org).tolower();
    if(org.startWith("data:")||org.startWith("javascript:")){
      return std.uri.encode("data:text/plain;charset=utf-8&#x2C;Security Error!!");
    }
    return org;
  }
  ///埋め込み用のエスケープ
  static string srcTargetEscape(string org){
    org=strip(org);
    if(!org.startWith("http:")&&!org.startWith("https:")&&!org.startWith("ftp:")){
      return std.uri.encode("data:text/plain;charset=utf-8&#x2C;Security Error!!");
    }
    return org;
  }
  ///id取得
  ///Returns: 常に6文字を返す
  static string getID(string org){
    return (.toString(cast(ulong)adler32(0,cast(void[])org),36u)~"______")[0..6];//Is this ok?
  }
}
